package com.apes.framework.plugin.condition.model;

import com.apes.framework.jpa.ApesBean;
import com.apes.framework.jpa.DefaultPersistListener;
import com.apes.framework.jpa.comment.Comment;
import com.apes.framework.jpa.generator.ApesGenerator;
import com.apes.framework.jpa.spec.JpaDsl;
import com.apes.framework.plugin.condition.PricingProcedureContext;
import com.apes.framework.util.*;
import com.apes.scm.masterdata.enumeration.model.Enumeration;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.annotations.DynamicInsert;
import org.hibernate.annotations.DynamicUpdate;
import org.hibernate.annotations.SelectBeforeUpdate;
import org.springframework.data.jpa.domain.Specification;

import javax.persistence.*;
import java.util.*;

import static java.util.stream.Collectors.groupingBy;


/**
 * 功能：条件表（存取）- 定义条件记录的关键字段，，包含一个或多个条件字段为关键字段，关键字段对应一个字段目录
 *
 * @author xul
 * @create 2018-11-06 17:10
 */
@Getter
@Setter
@SelectBeforeUpdate
@DynamicInsert
@DynamicUpdate
@Entity
@EntityListeners(DefaultPersistListener.class)
@Table(name = ConditionTable.CONST_TABLE_NAME)
public class ConditionTable implements ApesBean {
    public static final String CONST_TABLE_NAME = "tech_condition_table";
    public static final String TABLE_PREFIX = "z";
    public static final String Exec = "Exec";

    @Id
    @Column(name = "id", length = 32)
    @ApesGenerator(code = CONST_TABLE_NAME, name = "条件表")
    @Comment("编号")
    private String id;

    @Column(name = "name", nullable = false, length = 120)
    @Comment("描述")
    private String name;

    @Column(name = "has_date", nullable = false, columnDefinition = ApesConst.FIELD_BOOLEAN_FALSE)
    @Comment("有效期")
    private boolean hasDate = false;

    @Column(name = "stepped", nullable = false, columnDefinition = ApesConst.FIELD_BOOLEAN_FALSE)
    @Comment("阶梯")
    private boolean stepped = false;

    @Column(name = "vaild", nullable = false, columnDefinition = ApesConst.FIELD_BOOLEAN_FALSE)
    @Comment("合法性")
    private boolean vaild = false;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "field_Catalog_id", nullable = false, referencedColumnName = "id")
    @Comment("字段目录编码")
    private FieldCatalog fieldCatalog;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "usage_id", nullable = false, referencedColumnName = "id")
    @Comment("用途")
    private Usage usage;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "application_id", nullable = false, referencedColumnName = "id")
    @Comment("应用")
    private Application application;

    @OneToMany(mappedBy = "conditionTable", cascade = CascadeType.ALL)
    @OrderBy("id asc")
    private Set<ConditionTableItem> conditionTableItems = new HashSet();

    /**
     * 关键字段
     **/
    public Set<ConditionTableItem> getConditionTableItems() {
        for (ConditionTableItem item : conditionTableItems) {
            item.setConditionTable(this);
        }
        return conditionTableItems;
    }

    /**
     * 通用字段
     **/
    @OneToMany(mappedBy = "conditionTable", cascade = CascadeType.ALL)
    @OrderBy("id asc")
    private Set<ConditionTableGeneral> conditionTableGenerals = new HashSet();

    public Set<ConditionTableGeneral> getConditionTableGenerals() {
        if (conditionTableGenerals == null) return null;
        for (ConditionTableGeneral item : conditionTableGenerals) {
            item.setConditionTable(this);
        }
        return conditionTableGenerals;
    }

    /**
     * 阶梯字段
     **/
    @OneToMany(mappedBy = "conditionTable", cascade = CascadeType.ALL)
    @OrderBy("code asc")
    private Set<ConditionTableStepped> conditionTableSteppeds = new HashSet();

    public Set<ConditionTableStepped> getConditionTableSteppeds() {
        if (conditionTableSteppeds == null) return null;
        // 当阶梯字段有值的是否设置阶梯标记为 true
        if (conditionTableSteppeds.size() > 0) {
            this.setStepped(true);
        } else {
            this.setStepped(false);
        }
        for (ConditionTableStepped item : conditionTableSteppeds) {
            item.setConditionTable(this);
        }
        return conditionTableSteppeds;
    }

    /**
     * 查找条件记录
     */
    public <T> T findConditionRecord(final PricingProcedureContext context) {
        context.getCurrentProcedure().put("conditionTable", this);
        Specification criteria = toCriteria(context, true);
        if(criteria == null) return null;
        String className = getClassName(masterExecClassName());
        return (T) JpaUtil.getRepository(className).findOne(criteria).orElse(null);
    }

    public <T> List<T> findConditionRecord(T e, boolean vaild) {
        Specification criteria = toCriteria(e, vaild);
        if(criteria == null) return null;
        String className = getClassName(masterExecClassName());
        return JpaUtil.getRepository(className).findAll(criteria);
    }

    private Specification toCriteria(Object o, boolean vaild) {
        List<Map> items = new ArrayList<>();
        for (ConditionTableItem item : conditionTableItems) {
            String k = item.getConditionField().getId();
            Object v = null;
            if (o instanceof PricingProcedureContext) {
                v = ((PricingProcedureContext) o).getParameter(k);
            } else if (o instanceof ApesBean) {
                v = ReflectUtil.getFieldValue(o, k);
            }
            if (v == null) return null;
            items.add(MapUtil.mapper("field", k, "value", v, "operator", "EQ"));
        }
        if (vaild) {
            items.add(MapUtil.mapper("field", ConditionField.START_DATE_FIELD_NAME, "value", DateUtil.getDate(DateUtil.SHOW_DATE_FORMAT), "operator", "LTE"));
            items.add(MapUtil.mapper("field", ConditionField.END_DATE_FIELD_NAME, "value", DateUtil.getDate(DateUtil.SHOW_DATE_FORMAT), "operator", "GTE"));
        }
        return JpaDsl.toCriteria(items);
    }

    @Transient
    private boolean hasSlave() {
        return stepped;
    }

    @Transient
    private String packageName() {
        return String.format("%s", "com.apes.scm.dm");
    }

    @Transient
    public String masterClassName() {
        return String.format("%s%s", StringUtil.toUpperFirst(usage.getId()), id);
    }

    @Transient
    private String masterTableName() {
        return String.format("%s_%s_%s", TABLE_PREFIX, usage.getId(), id);
    }

    @Transient
    public String getClassName(String className) {
        return packageName() + ".model." + className;
    }

    @Transient
    public String masterExecClassName() {
        return String.format("%s%s%s", StringUtil.toUpperFirst(usage.getId()), Exec, id);
    }

    /**
     * 执行表：从表类名
     **/
    @Transient
    public String slaveExecClassName() {
        return String.format("%s%s%s%s", StringUtil.toUpperFirst(usage.getId()), Exec, id, "Items");
    }

    @Transient
    private String masterExecTableName() {
        return String.format("%s_%s_%s_%s", TABLE_PREFIX, usage.getId(), id, Exec);
    }

    @Transient
    public String indexs() {
        String indexs = "";
        String indexName = "";
        ConditionField field;
        for (ConditionTableItem item : conditionTableItems) {
            field = item.getFieldCatalogItem().getConditionField();
            if (indexs.equals("")) {
                indexs = field.columnName();
                indexName = field.columnName();
            } else {
                indexs = indexs + "," + field.columnName();
                indexName = indexName + "_" + field.columnName();
            }

        }
        indexs = indexs + "," + ConditionField.START_DATE_FIELD_NAME;
        return String.format(", indexes = { @Index(columnList = \"%s\", unique = true), @Index(columnList = \"source\") }", indexs);
    }

    public Map<String, String> createEntiy(List<FieldCatalogItem> generalFieldList) {
        List<ConditionField> fields = new ArrayList<>();
        String indexs = "";
        ConditionField field;
        //关键字段
        for (ConditionTableItem item : conditionTableItems) {
            field = item.getFieldCatalogItem().getConditionField();
            if (item.isMain()) {
                field.setAttribution(CreateEntity.MASTER);
            } else {
                field.setAttribution(CreateEntity.ITEM);
            }
            field.setAttributionExec(CreateEntity.MASTER);
            field.setRequired(true);
            fields.add(field);
            if (indexs.equals("")) {
                indexs = field.columnName();
            } else
                indexs = indexs + "," + field.columnName();
        }
        field = createConditionField("conditionTypes", "com.apes.framework.plugin.condition.model.ConditionTypes", "ConditionTypes", CreateEntity.MASTER); //条件类型
        field.setRequired(true);
        fields.add(field);
        field = createConditionField("conditionTable", "com.apes.framework.plugin.condition.model.ConditionTable", "ConditionTable", CreateEntity.MASTER);
        field.setRequired(true);
        fields.add(field);

        //通用字段
        conditionTableGenerals.stream().forEach(
                (general) -> {
                    generalFieldList.stream().filter(
                            catalogItem -> catalogItem.getId().equals(general.getFieldCatalogItem().getId())
                    ).findFirst().ifPresent(
                            (item) -> {
                                item.setMain(general.isMain());
//                                item.setExec(general.isExec());
//                                item.setExecMain(general.isExecMain());
                            }
                    );
                }
        );
        //创建通用字段
        if (isStepped()) {
            generalFieldList.remove(generalFieldList.stream().filter(item -> "amount".equals(item.getConditionField().getId())).findAny().get());
        }
        for (FieldCatalogItem item : generalFieldList) {
            field = item.getConditionField();
            if (item.isMain()) {
                field.setAttribution(CreateEntity.MASTER);
            } else {
                field.setAttribution(CreateEntity.ITEM);
            }
            field.setRequired(item.isRequired());
            field.setAttributionExec(execMainOrItem(item));//确定是否在执行表
            fields.add(field);
        }
        // 创建阶梯字段
        for (ConditionTableStepped conditionTableStepped : conditionTableSteppeds) {
            // 阶梯字段
            field = createConditionField(conditionTableStepped.stepKeyField(), "double", "double", CreateEntity.ITEM);
            fields.add(field);
            // 阶梯值字段
            field = createConditionField(conditionTableStepped.stepValueField(), "double", "double", CreateEntity.ITEM);
            fields.add(field);
        }

        Map<String, String> beanMap = new HashMap<>();
        //操作表
        Map<String, List<ConditionField>> map = fields.stream().collect(groupingBy(ConditionField::getAttribution));
        String className = masterClassName();
        String tableName = masterTableName();
        dealData(beanMap, className, tableName, map, true);
        //执行表
        field = createConditionField("source", "java.lang.String", "String", CreateEntity.ITEM);
        field.setAttributionExec(CreateEntity.MASTER);
        fields.add(field);
        if (conditionTableSteppeds.size() > 0) {
            field = createConditionField(ConditionField.STEP_FIELD_NAME, "double", "double", CreateEntity.ITEM);
            field.setAttributionExec(CreateEntity.ITEM);
            fields.add(field);

            field = createConditionField(ConditionField.AMOUNT_FIELD_NAME, "double", "double", CreateEntity.ITEM);
            field.setAttributionExec(CreateEntity.ITEM);
            fields.add(field);
        }
        Map<String, List<ConditionField>> execMap = fields.stream().filter(field1 -> !field1.getAttributionExec().equals("")).collect(groupingBy(ConditionField::getAttributionExec));
        String execClassName = masterExecClassName();
        String execTableName = masterExecTableName();
        dealData(beanMap, execClassName, execTableName, execMap, isStepped());

        return beanMap;
    }

    private void dealData(Map<String, String> beanMap, String className, String tableName, Map<String, List<ConditionField>> execMap, boolean isMultiple) {
        for (String key : execMap.keySet()) {
            if (key.equals(CreateEntity.MASTER)) {
                beanMap.put(getClassName(className), createScript(key, className, tableName, execMap.get(key), isMultiple));
            } else if (key.equals(CreateEntity.ITEM)) {
                beanMap.put(packageName() + ".model." + className + "Items", createScript(key, className, tableName, execMap.get(key), isMultiple));
            }
        }
    }

    private String execMainOrItem(FieldCatalogItem fieldCatalogItem) {
        if (fieldCatalogItem.isExec() && fieldCatalogItem.isExecMain()) {
            return CreateEntity.MASTER;
        } else if (fieldCatalogItem.isExec() && !fieldCatalogItem.isExecMain()) {
            return CreateEntity.ITEM;
        } else {
            return "";
        }
    }

    private String createScript(String type, String className, String tableName, List<ConditionField> fields, boolean isMultiple) {
        StringBuffer importEntity = new StringBuffer();
        StringBuffer properties = new StringBuffer();
        StringBuffer method = new StringBuffer();
        for (ConditionField field : fields) {
            importEntity.append(field.importEntityScript());
            properties.append(field.propertyScript());
            method.append(field.methodScript());
        }
        return CreateEntity.createModel(packageName(), className, tableName,
                importEntity.toString(), properties.toString(), method.toString(), indexs(), type, isMultiple);
    }

    private ConditionField createConditionField(String id, String type, String code, String attr) {
        ConditionField field = new ConditionField();
        field.setId(id);
        field.setAttribution(attr);

        Enumeration enumeration = new Enumeration();
        enumeration.setId(type);
        enumeration.setCode(code);
        field.setType(enumeration);
        return field;
    }

}
